/* ************************************
 * TreeGen
 * Code / tree saving functions
 * ************************************
 */

// minimum icicle density
static final float MIN_ICICLE_DENSITY			= 0.05;

// gaussian function
static final float GAUSSIAN_MEAN				= 350;

static final float GREATER_GAUSSIAN_DEVIATION		= 120.0;
static final float GREATER_GAUSSIAN_DEVIATION_SQ_2	= 2.0 * sq(GREATER_GAUSSIAN_DEVIATION);

static final float SMALLER_GAUSSIAN_DEVIATION		= 120;
static final float SMALLER_GAUSSIAN_DEVIATION_SQ_2	= 2.0 * sq(SMALLER_GAUSSIAN_DEVIATION);

static final float GAUSSIAN_WEIGHT			= 0.5;

// model uncertainty in node links density 
static final int MIN_RANGE_SUBTRACT = 1;
static final int MAX_RANGE_ADD = 1;
static final boolean MODEL_DENSITY = true;

// strings for writing lisp code to
String displayCode = null;
String memoryCode = null;

// hashmap to resolve duplicate names
HashMap<String, Boolean> duplicate_names;

class IVector
{
	int x, y;
	IVector(int _x, int _y)
	{
		x = _x;
		y = _y;
	}
	int first() { return x; }
	int second() { return y; }
}

float evalGaussian(float x)
{
	if (x <= GAUSSIAN_MEAN)
	{
		return exp( -sq(x - GAUSSIAN_MEAN) / SMALLER_GAUSSIAN_DEVIATION_SQ_2 ); 
	}
	else
	{
		return exp( -sq(x - GAUSSIAN_MEAN) / GREATER_GAUSSIAN_DEVIATION_SQ_2 );
	}
}

float evalChildCountPotential(int count)
{
	float mue = (float) count + 0.5;
	return exp( mue / 2.9 ) - exp( -mue / 700.0 );
}


void writeTree(Node tree, CachedFileWriter output, int tabLength)
{
	String tabs = generateTabs( tabLength );
	
	output.writeString( tabs + tree.txt );
	
	// write node
	if (tree.children.size() > 0)
	{
		output.writeString( " {\n" );
		for (int i = 0; i < tree.children.size(); i++)
		{
			writeTree( tree.children.get(i), output, tabLength + 1 );
		}
		output.writeString( tabs + "}" );
	}
	output.writeString( "\n" );	
}

IVector writeLispTree(Node tree, int tabLength)
{
	int X = tree.x + 10;
	int Y = tree.y + 10;
	
	String tab = generateTabs( tabLength );
	
	// add current node
	String txt = tab + "(add-text-to-exp-window :text \"" + tree.txt + "\" :x " + (X - 5) + " :y " + (Y - 5) + " :width 10 :height 10)";
	displayCode += (txt + "\n");

	// if node have children, then add a 'red' marker node to calculate the number of children
	if (tree.children.size() > 0 && MODEL_DENSITY)
	{
		txt = tab + "(add-text-to-exp-window :text \"" + tree.txt + "\" :x " + (X - 5) + " :y " + (Y + 5) + " :color 'red :width 10 :height 10)";
		displayCode += (txt + "\n");
	}
		
	// now go through children
	if (tree.children.size() > 0)
	{
		int childrenBeginX = MAX_INT;
		int childrenEndX = MIN_INT;
		
		Node lastChild = null;
		for (int i = 0; i < tree.children.size(); i++)
		{
			Node child = tree.children.get( i );
			lastChild = child;
			
			// add children
			IVector xExtent = writeLispTree( child, tabLength + 1 );
			
			childrenBeginX = min(childrenBeginX, xExtent.first());
			childrenEndX = max(childrenEndX, xExtent.second());
			
			// add declarative memory for tracing children
			String dmTXT = "(" + child.txt + "-p-trace\t\t ISA \t\t parent-trace node \"" + child.txt.toLowerCase() + "\" x " + (X - 5 + 8) + " y " + (Y - 5 + 5) + ")";
			memoryCode += (dmTXT + "\n");
			 
		}
		
		// add declarative memory for child range of current node
		int bx = min(childrenBeginX, childrenEndX);
		int ex = max(childrenBeginX, childrenEndX);
		
		String dmChildRange = "(" + tree.txt + "-child-range\t\t ISA \t\t child-range node \"" + tree.txt.toLowerCase() + "\"" + 
			" bx " + bx + " ex " + ex + 
			" y "  + (lastChild.y - 5 + 10) + " by " + (lastChild.y - 5 + 10) + " ey " + (lastChild.y + 5 + 10) + 
			" parent-x " + (X - 5 + 8) + " parent-y " + (Y - 5 + 5) + ")";
		
		memoryCode += (dmChildRange + "\n");
		
		// add another piece of DM to model density of links for this parent node
		String dmDensity = "(" + tree.txt + "-density\t\t ISA \t\t node-density node \"" + tree.txt.toLowerCase() + "\"" +
			" min-range " + (tree.children.size() - MIN_RANGE_SUBTRACT) +
			" max-range " + (tree.children.size() + MAX_RANGE_ADD) + 
			" x " + (X - 5 + 8) + " y " + (Y + 5 + 5) + ")\n";
		
		
		
		// add another density node for icicle mode
		// calculate icicle density and weight it against the actual width of node (in pixels)
		float cD = (float) tree.children.size() / (float) tree.nodeWidth;
		float childPotential = evalChildCountPotential( tree.children.size() );
		float gD = evalGaussian( (float) tree.nodeWidth * NODE_WIDTH );
		
		//float iD = GAUSSIAN_WEIGHT * gD + (1.0 - GAUSSIAN_WEIGHT) * cD;
		float iD = gD * childPotential;
		iD = max(iD, MIN_ICICLE_DENSITY);
		
		dmDensity += "(" + tree.txt + "-density-icicle\t ISA \t\t node-density-icicle node \"" + tree.txt.toLowerCase() + "\"" +
			" density " + iD + 
			" x " + (X - 5 + 8) + " y " + (Y + 5 + 5) + ")\n";
		
		memoryCode += dmDensity;
		
		return new IVector( childrenBeginX, childrenEndX );

	}
	else
	{
		return new IVector((X - 5), (X + 5));

	}
}

void writeLispCode(Node tree, CachedFileWriter output)
{
	// clear out memory and display buffers
	displayCode = "\n\n ; dispaly code \n ; ==================== \n\n";
	memoryCode = "\n\n ; memory code \n ; ===================== \n\n";
	
	writeLispTree( tree, 0);
	
	// write to file
	output.writeString( displayCode );
	output.writeString( memoryCode );
}

void resolveDuplicateNames(Node tree)
{
	if (duplicate_names.get( tree.txt ) != null)
	{
		
		// keep the second letter
		tree.txt =  "" + tree.txt.charAt(1) + (char) (int(random(10)) + '0') ;
	}
	duplicate_names.put( tree.txt, true );
	
	for (int i = 0; i < tree.children.size(); i++)
	{
		resolveDuplicateNames( tree.children.get(i) );
	}
	
}

void convertTXTToLisp(String directory)
{
	// list all files in directory
	File dir = new File( dataPath( directory ) );
	String[] children = dir.list();
	
	// print some stuff
	println("Converting directory: " + dataPath(directory));
	println("There are " + children.length + " files in directory...\n");
	
	for (int i = 0; i < children.length; i++)
	{
		String childName = children[i];
		
		// process only txt files
		String extension = childName.substring( childName.length() - 1 - 2 );
		String fileName = childName.substring( 0, childName.length() - 1 - 2 );
		
		if (extension.compareToIgnoreCase("txt") != 0)
		{
			// skip this file
			println("Skipping file " + childName + ". Extension: " + extension);
			continue;
		}
		
		// load the TXT
		CachedFileReader input = new CachedFileReader( dataPath( directory + "/" + childName ), null );
		println("\t * processing " + childName);
		Node tree = readTree( input, null );
		input.closeFile();
		
		duplicate_names = new HashMap<String, Boolean>();
		resolveDuplicateNames( tree );
		
		// lay it out
		layoutIcicle( tree );
		layoutGraphNodes( tree, 0, 0 );
		
		// convert to lisp
		CachedFileWriter output = new CachedFileWriter( dataPath( directory + "/" + fileName + "lisp" ), null );
		writeLispCode( tree, output );
		output.closeFile();
	}
		
}


